Section top bar
canonComposable bar with title, chips, search, dividers and spacer.
Four screens hand-roll the identity instead of SectionTopBarTitle — decision A6. Phone two-step (AD 27 Sep 2026, acrm !3997, fix/nastaveni-mobil-dvoukrok): Nastavení (SettingsLayout.tsx) below the 720px breakpoint drops the side rail and becomes ⌘K's own two-step — `/nastaveni` with no section shows only the grouped menu full-width (`.nastaveni-page.m-menu`), `/nastaveni/<sekce>` shows only that section's `SectionTopBar` (`flat`) and body (`.m-section`) with a quiet `IconButton icon="arrow-left"` (`.set-back`) as the bar's first item, wired to `navigate('/nastaveni')`. Desktop keeps rail + content side by side unchanged. Side rail at ≥720px would take ~260px on a 390px phone and clip every section's fields — the same two-step shape as the ⌘K palette's list→detail step, not a new pattern. acrm !4029 (27 Sep 2026): new app-only layer apps/web/src/ds/section-top-bar.css (home for bar rules; ds.css is over the 100 kB ratchet and must not grow) — a long name in the bar stays on one line with an ellipsis and SectionTopBarTitle carries `title` with the full name; switch labels in the bar do not wrap. acrm !4044 (27 Sep 2026): the record view switcher on person/company (Activities / Profile) is the canonical `.tchip` with chevron (RULES §8: a switcher in the bar = .tchip + menu), the bespoke `.rvsw-*` was deleted. Menu items without description text (acrm !4051). Phone layout, Attio pattern (owner 2026-09-28, acrm !4088): actions as data — primaryAction {label, icon, onClick, kind} → Fab below 720 px (round only for kind 'create'), secondaryActions (menuOnly, danger, phoneOnly) → one ⋮ bottom sheet, closeAction last; row 1 = title + ⋮, row 2 = tools (view picker left, Filtr/Řadit right). Desktop unchanged. PageHeader forwards the same props. 14 Oct 2026 (owner "schvaleno" 24b v4, proto/detail-jednotne@9600c16a2, acrm !5055; RULES §26): record detail header = ↑↓ · name · ⋮ · ✕ with NO blue primary. Rare actions stay in context (Nový obchod in Nejbližší aktivita + Obchody tab; project Aktualizace in its section); an order shows one button only for the step it waits for. Dev check 14 Oct 1440 + 390: person, company, project, order — 0 primaries in the header, the project label chip gone. Registry repaired 17 Oct 2026 (review F5): PageHeader, RecordCloseButton live in apps/web (see files), not exported by @acrm/ui; dropped from react[]. 20 Oct 2026 (ACRM-569, proto/komunikace-menu-zobrazit-vse@0b7180f3a): `SectionTopBarTitle dot` — identity by colour with no glyph (`.pipe-dot`, a 12 px circle inside the `--tile-sm` frame, CSS in apps/web/src/ds/section-top-bar-dot.css); used by a mail label list. Story: "With dot".
Used in 64 files · 56 areas · 118× — components/ListSearch, components/accounting, components/agent-desk, components/datamart, components/segment, pages/AccountingInvoiceDetailPage …
@acrm/ui <SectionTopBar> <SectionTopBarChip> <SectionTopBarSearch> <SectionTopBarDivider> <SectionTopBarSpacer>.tbar .tchip .acrm-input .vspace +7SourceStoryLegacy card Section top bar.htmlKeyboard
- Every control in the bar is a real
<button>or<input>; Tab walks identity → view controls → search → actions → primary action, which is the reading order of the bar. - Chips that open a menu carry
aria-haspopupandaria-expandedthrough the control that opens them; Escape closes the menu (Popover's contract). - The search is a normal input: typing filters, Escape is left to the screen.
- Inline rename: Enter commits, Escape cancels, and both stop propagating so the record's own Enter/Escape handlers do not fire as well. Blur commits only if Enter has not already done it (a latch prevents double-saving).
- The bar has no shortcut of its own; a shortcut belongs to the action that answers to it.
ARIA
- The bar is a plain container; its accessible structure comes from the controls inside. The identity is a heading-level text, not an
<h1>— the page supplies its own heading semantics. SectionTopBarChipsetsaria-haspopup="menu"andaria-expandedonly when it is a menu trigger (chevron/open), so a plain toggle chip is not announced as a menu.- The search input is named by
ariaLabel; the placeholder is not a name. - Rename mode swaps the text for an input with an accessible name from
renameAriaLabel; whilerenamingthe input is disabled and cannot be reopened. - Dev-only warnings guard the semantics that matter: identity missing,
onRenamecombined with a dropdown,iconcombined with a click handler.
- Phone ⋮:
IconButtonnamed „Další akce" witharia-haspopup="menu"/aria-expanded; its bottom sheet is titled „Další akce". - Filtr icon button on a phone: its visible label is hidden, so the name carries both — „Filtr, aktivní podmínky: N".
Focus
.tbar-pos.is-linkand.tbar-markdeclare their own:focus-visiblering inds.css; everything else in the bar is a<button>and takes the foundation ring (outline: 2px solid var(--accent); outline-offset: 1px).- The search box shows focus through
:focus-withinon the wrapper (accent border) rather than a ring on the input. - Entering rename mode moves focus into the input and selects nothing — the caret lands where the user clicked.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Identity name: ink on
--surface-100= 17.59:1 light, 14.53:1 dark. - Chip value
.vink = 17.59:1 light; chip key.k--fg-muted= 2.85:1 light, 3.63:1 dark — below AA for text, the value carries the meaning. - Chip border
--border-strongon--surface-100= 1.38:1 light — hairline, below the 3:1 non-text minimum. - Search placeholder
--fg-muted= 2.85:1 light. - Focus ring
--accent= 4.44:1 light, 3.83:1 dark — passes 3:1.
Phone view row (acrm !4114)
- Icon-only tools in row 2 (Seskupit, Řadit, search) have an
aria-label; when Seskupit or Řadit is off its default, the label and thetitlecarry the chosen option („Řadit: Termín") — the accent dot is decorative and never the only signal. - The tools open a bottom sheet (
role="menu", rowsmenuitemradiowitharia-checked); the sheet is closed by „Hotovo", the scrim or Escape. - Expanded search has its own
aria-labeland a labelled × („Zavřít"); closing clears the query.
Known limits
- Muted keys and placeholders sit at 2.85:1 in light theme.
- No disabled style exists for a chip or the search; a control that cannot be used has to be left out instead.
- On a narrow screen the bar scrolls sideways: a control can be off-screen with no visual hint other than the cut edge.
- Screens that hand-roll the identity (status star, logo trigger, colour swatch) bypass the component's guardrails, so their semantics depend on the screen.
Source: docs/section-top-bar/section-top-bar.a11y.md · GitLab